package com.afollestad.library.callback;

import com.afollestad.library.utils.PixelUtil;

import ohos.agp.components.Component;
import ohos.agp.components.ListContainer;
import ohos.agp.utils.Rect;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.eventhandler.InnerEvent;
import ohos.multimodalinput.event.MmiPoint;
import ohos.multimodalinput.event.TouchEvent;

/**
 * description : DragSelectTouchListener 拖拽时的触碰监听
 *
 * @since 2021-03-12
 */
public class DragSelectTouchListener {
    private static final int AUTO_SCROLL_DELAY = 25;

    private ListContainer listContainer = null;
    private int touchItemPosition = 0;
    private int lastDraggedIndex = -1;
    private int initialSelection = 0;
    private Boolean dragSelectActive = false;
    private int minReached = 0;
    private int maxReached = 0;
    private int hotspotTopBoundStart = 0;
    private int hotspotTopBoundEnd = 0;
    private int hotspotBottomBoundStart = 0;
    private int hotspotBottomBoundEnd = 0;
    private Boolean inTopHotspot = false;
    private Boolean inBottomHotspot = false;
    private int autoScrollVelocity = 0;
    private Boolean isAutoScrolling = false;
    private int hotspotHeight = 0;
    private int hotspotOffsetTop = 0;
    private int hotspotOffsetBottom = 0;
    private Mode mode = Mode.RANGE;
    private DragSelectReceiver mReceiver;

    // 创建一个自动控制的EventRunner
    private EventRunner eventRunner = EventRunner.getMainEventRunner();
    private MyEventHandler autoScrollHandler = new MyEventHandler(eventRunner);
    private Runnable autoScrollRunnable = new Runnable() {
        @Override
        public void run() {
            if (inTopHotspot) {
                listContainer.scrollBy(0, -autoScrollVelocity);
                autoScrollHandler.postTask(this, (long) AUTO_SCROLL_DELAY);
            }
            if (inBottomHotspot) {
                listContainer.scrollBy(0, autoScrollVelocity);
                autoScrollHandler.postTask(this, (long) AUTO_SCROLL_DELAY);
            }
        }
    };

    private DragSelectTouchListener(ListContainer list) {
        this.listContainer = list;
        if (listContainer.getItemProvider() instanceof DragSelectReceiver) {
            mReceiver = (DragSelectReceiver) listContainer.getItemProvider();
        }
        if (mReceiver == null) {
            throw new SecurityException("请先给listContainer设置实现DragSelectReceiver接口的provider！");
        }
        mReceiver.setOnItemLongClickListener(position -> {
            touchItemPosition = position;
            setIsActive(true, touchItemPosition);
        });
        hotspotHeight = (int) PixelUtil.vp2px(60);
    }

    /**
     * 设置选择模式
     *
     * @param mode 选择模式
     */
    public void setMode(Mode mode) {
        this.mode = mode;
        setIsActive(false, -1);
    }

    /**
     * 创建DragSelectTouchListener拖拽选择监听
     *
     * @param listContainer 拖拽选择监听
     * @return DragSelectTouchListener拖拽选择监听
     */
    public static DragSelectTouchListener create(ListContainer listContainer) {
        return new DragSelectTouchListener(listContainer);
    }

    /**
     * 设置是否是长按选择模式
     *
     * @param active           是否是长按选择模式
     * @param initialSelection 被长按的item的position 为-1时说明非长按模式
     */
    public void setIsActive(boolean active, int initialSelection) {
        if (active && dragSelectActive) {
            return;
        }
        this.lastDraggedIndex = -1;
        this.minReached = -1;
        this.maxReached = -1;
        this.autoScrollHandler.removeTask(autoScrollRunnable);
        this.notifyAutoScrollListener(false);
        this.inTopHotspot = false;
        this.inBottomHotspot = false;
        if (!active) {
            // Don't do any of the initialization below since we are terminating
            this.initialSelection = -1;
            return;
        }
        mReceiver.setItemSelect(initialSelection, true);
        this.dragSelectActive = active;
        this.initialSelection = initialSelection;
        this.lastDraggedIndex = initialSelection;
    }

    private void notifyAutoScrollListener(boolean scrolling) {
        if (this.isAutoScrolling == scrolling) {
            return;
        }
        this.isAutoScrolling = scrolling;
    }

    private void onDragSelectionStop() {
        dragSelectActive = false;
        inTopHotspot = false;
        inBottomHotspot = false;
        autoScrollHandler.removeTask(autoScrollRunnable);
        this.notifyAutoScrollListener(false);
    }

    // 根据手指移动所在的坐标点获取itemPosition
    private int getListContainerItemByTouchEvent(ListContainer listContainer, TouchEvent touchEvent) {
        int result = -1;
        float touchX = touchEvent.getPointerPosition(touchEvent.getIndex()).getX();
        float touchY = touchEvent.getPointerPosition(touchEvent.getIndex()).getY();
        int childCount = listContainer.getChildCount();
        for (int i = 0; i < childCount; i++) {
            Component componentAt = listContainer.getComponentAt(i);
            if (componentAt == null) {
                return result;
            }
            Rect componentRect = componentAt.getComponentPosition();
            int top = componentRect.top;
            int bottom = componentRect.bottom;
            int left = componentRect.left;
            int right = componentRect.right;
            if (touchY >= top && touchY <= bottom && touchX <= right && touchX >= left) {
                result = i;
                return result;
            }
        }
        return result;
    }

    /**
     * touch监听触发的回调
     *
     * @param listContainer 被监听touch的listContainer
     * @param touchEvent    touchEvent
     * @return return
     */
    public boolean onTouch(ListContainer listContainer, TouchEvent touchEvent) {
        boolean adapterIsEmpty = listContainer.getChildCount() == 0;
        boolean result = dragSelectActive && !adapterIsEmpty;
        if (result) {
            this.listContainer = listContainer;
            if (hotspotHeight > -1) {
                hotspotTopBoundStart = hotspotOffsetTop;
                hotspotTopBoundEnd = hotspotOffsetTop + hotspotHeight;
                hotspotBottomBoundStart = listContainer.getEstimatedHeight() - hotspotHeight - hotspotOffsetBottom;
                hotspotBottomBoundEnd = listContainer.getEstimatedHeight() - hotspotOffsetBottom;
            }
        }

        if (result && touchEvent.getAction() == TouchEvent.PRIMARY_POINT_UP) {
            onDragSelectionStop();
        }

        touchItemPosition = getListContainerItemByTouchEvent(listContainer, touchEvent);
        if (result) {
            // 触摸事件应该被拦截
            if (touchItemPosition == -1) {
                return false;
            }
            MmiPoint eventPosition = touchEvent.getPointerPosition(touchEvent.getIndex());
            switch (touchEvent.getAction()) {
                case TouchEvent.PRIMARY_POINT_UP:
                    onDragSelectionStop();
                    return true;
                case TouchEvent.POINT_MOVE:
                    if (hotspotHeight > -1) {
                        // Check for auto-scroll hotspot
                        float touchY = eventPosition.getY();
                        if (touchY >= hotspotTopBoundStart && touchY <= hotspotTopBoundEnd) {
                            inBottomHotspot = false;
                            if (!inTopHotspot) {
                                inTopHotspot = true;
                                autoScrollHandler.removeTask(autoScrollRunnable);
                                autoScrollHandler.postTask(autoScrollRunnable, AUTO_SCROLL_DELAY);
                                this.notifyAutoScrollListener(true);
                            }
                            double simulatedFactor = (hotspotTopBoundEnd - hotspotTopBoundStart);
                            double simulatedY = (double) touchY - hotspotTopBoundStart;
                            autoScrollVelocity = ((int) (simulatedFactor - simulatedY)) / 2;
                        } else if (touchY >= hotspotBottomBoundStart && touchY <= hotspotBottomBoundEnd) {
                            inTopHotspot = false;
                            if (!inBottomHotspot) {
                                inBottomHotspot = true;
                                autoScrollHandler.removeTask(autoScrollRunnable);
                                autoScrollHandler.postTask(autoScrollRunnable, AUTO_SCROLL_DELAY);
                                this.notifyAutoScrollListener(true);
                            }
                            double simulatedY = (double)touchY + (double) hotspotBottomBoundEnd;
                            double simulatedFactor = hotspotBottomBoundStart + hotspotBottomBoundEnd;
                            autoScrollVelocity = ((int) (simulatedY - simulatedFactor)) / 2;
                        } else {
                            if (inTopHotspot || inBottomHotspot) {
                                autoScrollHandler.removeTask(autoScrollRunnable);
                                this.notifyAutoScrollListener(false);
                                inTopHotspot = false;
                                inBottomHotspot = false;
                            }
                        }
                    }
                    // Drag selection logic
                    if (mode == Mode.PATH && touchItemPosition != -1) {
                        // Non-default mode, we select exactly what the user touches over
                        if (lastDraggedIndex == touchItemPosition) {
                            return true;
                        }
                        lastDraggedIndex = touchItemPosition;
                        mReceiver.setItemSelect(lastDraggedIndex);
                        return true;
                    }
                    if (mode == Mode.RANGE && touchItemPosition != -1 && lastDraggedIndex != touchItemPosition) {
                        lastDraggedIndex = touchItemPosition;
                        if (minReached == -1) {
                            minReached = lastDraggedIndex;
                        }
                        if (maxReached == -1) {
                            maxReached = lastDraggedIndex;
                        }
                        if (lastDraggedIndex > maxReached) {
                            maxReached = lastDraggedIndex;
                        }
                        if (lastDraggedIndex < minReached) {
                            minReached = lastDraggedIndex;
                        }
                        selectRange(initialSelection, lastDraggedIndex, minReached, maxReached);
                        if (initialSelection == lastDraggedIndex) {
                            minReached = lastDraggedIndex;
                            maxReached = lastDraggedIndex;
                        }
                    }
                    return true;
            }
        }
        return result;
    }

    private void selectRange(int from, int to, int min, int max) {
        if (from == to) {
            // Finger is back on the initial item, unselect everything else
            for (int i = min; i <= max; i++) {
                if (i == from) {
                    continue;
                }
                mReceiver.setItemSelect(i, false);
            }
            return;
        }
        if (to < from) {
            // When selecting from one to previous items
            for (int i = to; i <= from; i++) {
                mReceiver.setItemSelect(i, true);
            }
            if (min > -1 && min < to) {
                // Unselect items that were selected during this drag but no longer are
                for (int i = min; i < to; i++) {
                    if (i == from) {
                        continue;
                    }
                    mReceiver.setItemSelect(i, false);
                }
            }
            if (max > -1) {
                for (int i = from + 1; i <= max; i++) {
                    mReceiver.setItemSelect(i, false);
                }
            }
        } else {
            // When selecting from one to next items
            for (int i = from; i <= to; i++) {
                mReceiver.setItemSelect(i, true);
            }
            if (max > -1 && max > to) {
                // Unselect items that were selected during this drag but no longer are
                for (int i = to + 1; i <= max; i++) {
                    if (i == from) {
                        continue;
                    }
                    mReceiver.setItemSelect(i, false);
                }
            }
            if (min > -1) {
                for (int i = min; i < from; i++) {
                    mReceiver.setItemSelect(i, false);
                }
            }
        }
    }

    /**
     * 选择模式 RANGE:根据滑动范围进行选择； PATH：根据滑动路径进行选择
     */
    public enum Mode {
        RANGE,
        PATH
    }

    private static class MyEventHandler extends EventHandler {
        private MyEventHandler(EventRunner runner) {
            super(runner);
        }
        // 重写实现processEvent方法

        @Override
        public void processEvent(InnerEvent event) {
            super.processEvent(event);
        }
    }
}
